package sundial;

/** Java Imports */
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.TextField;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JRadioButton;
import javax.swing.ButtonGroup;


/**
 * This class implements the overall GUI used for the Sundial Creation program.  All modules
 * are integrated in this class.
 * 
 * @author Takanori Shiotsu
 * @author Reyn Matsumoto
 *
 */
public class SundialInterface implements ActionListener {
	
	//gui
	private JFrame gui;
	
	//inputs
	private JPanel inputs;
	private JPanel inputLatitude;
	private JPanel inputLongitude;
	private JPanel inputDate;
	private JPanel inputDLS;
	
	//buttons
	private JPanel buttons;
	private JButton createButton;
	private JButton printButton;
	private JRadioButton dlsNo;
	private JRadioButton dlsYes;
	
	//text
	private JTextArea instructions;
	private TextField tfLatitude;
	private TextField tfLongitude;
	private TextField tfDate;
	
	//labels
	private JLabel latitudeLabel;
	private JLabel longitudeLabel;
	private JLabel dateLabel;
	private JLabel dlsLabel;
	
	//calculations
	private SundialCompute computations;
	
	//output
	private JPanel drawPanel;	
	private BasicPanel drawing;
	private JFrame drawFrame;
	private JFrame gnomon;
	private JTabbedPane outputs;
	
	//booleans
	private boolean dls = false;
	private boolean sundialExists;
	
	/*
	 * Initializes all GUI components and adds them to JFrame gui.
	 */
	public void init() {
		gui = new JFrame("Sundial");
		sundialExists = false;	
				
		//Used later
		outputs = new JTabbedPane();
		
		//Text customization
		instructions = new JTextArea("This program creates a sundial for you to print.  Steps:\n1) Simply enter your latitude, longitude, " +
				   					 "and the current date and hit create to make the sundial base and gnomon.  \n     You may use the tabs below to " +
				   					 "view the sundial base and gnomon. \n     For latitude, North = Positive and South = Negative" +
				   					 "  |  For longitude, East = Positive and West = Negative \n2) Simply hit print " +
				   					 "to bring up a printing prompt.  \n3) Select portrait and hit okay.  \n4) For the first prompt, " +
				   					 "hit okay to print out the sundial base. \n5) For the second prompt, hit okay to print out the gnomon. \n" +
				   					 "6) Cut out the gnomon with some white space left on the bottom, fold the white space and " +
				   					 "paste or tape the gnomon so that it\n     aligns with the 12 o'Clock hour line on the base.");
		instructions.setEditable(false);
		instructions.setLineWrap(true);
		instructions.setWrapStyleWord(true);
		instructions.setBackground(new Color(255, 255, 200));
		instructions.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		instructions.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(4, 4, 4, 4, Color.black), BorderFactory.createEmptyBorder(4, 4, 4, 4)));
		
		latitudeLabel = new JLabel("Latitude: ");
		latitudeLabel.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		longitudeLabel = new JLabel("Longitude: ");
		longitudeLabel.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		dateLabel = new JLabel("Date: ");
		dateLabel.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		dlsLabel = new JLabel("Daylight Savings: ");
		dlsLabel.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		
		//Makes text fields match in size
		latitudeLabel.setPreferredSize(dlsLabel.getPreferredSize());
		longitudeLabel.setPreferredSize(dlsLabel.getPreferredSize());
		dateLabel.setPreferredSize(dlsLabel.getPreferredSize());
		
		tfLatitude = new TextField(20);
		tfLongitude = new TextField(20);
		tfDate = new TextField(20);
		
		tfLatitude.setText("Range: -90.0 to 90.0");
		tfLatitude.setFont(new Font("Lucida Grande", Font.BOLD, 12));
		tfLongitude.setText("Range: -180.0 to 180.0");
		tfLongitude.setFont(new Font("Lucida Grande", Font.BOLD, 12));
		
		tfDate.setText("MMDDYYYY");
		tfDate.setFont(new Font("Lucida Grande", Font.BOLD, 12));
		
		//GUI customization
		gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		gui.setSize(930, 850);
		gui.setLayout(new BorderLayout(4,4));
		gui.setResizable(false);
		gui.setLocationRelativeTo(null);
		
		//Radio buttons
		dlsNo = new JRadioButton("No");
		dlsNo.setFont(new Font("Lucida Grande", Font.BOLD, 12));
		dlsNo.setSelected(true);		
		dlsYes = new JRadioButton("Yes");
		dlsYes.setFont(new Font("Lucida Grande", Font.BOLD, 12));
		
		ButtonGroup group = new ButtonGroup();
		group.add(dlsNo);
		group.add(dlsYes);
		
		dlsNo.addActionListener(this);
		dlsYes.addActionListener(this);
		
		//Input field customization
		inputs = new JPanel(new GridLayout(4, 0));
		
		inputLatitude = new JPanel();
		inputLongitude = new JPanel();
		inputDate = new JPanel();
		inputDLS = new JPanel();
		
		inputLatitude.add(latitudeLabel);
		inputLatitude.add(tfLatitude);
		inputLatitude.setBackground(new Color(255, 255, 200));
		
		inputLongitude.add(longitudeLabel);
		inputLongitude.add(tfLongitude);
		inputLongitude.setBackground(new Color(255, 255, 200));
		
		inputDate.add(dateLabel);
		inputDate.add(tfDate);
		inputDate.setBackground(new Color(255, 255, 200));
		
		inputDLS.add(dlsLabel);
		inputDLS.add(dlsNo);
		inputDLS.add(dlsYes);
		inputDLS.setBackground(new Color(255, 255, 200));
		
		inputs.add(inputLatitude);
		inputs.add(inputLongitude);
		inputs.add(inputDate);
		inputs.add(inputDLS);
		inputs.setBorder(BorderFactory.createMatteBorder(4, 4, 4, 4, Color.black));
		
		gui.add(instructions, BorderLayout.PAGE_START);
		
		gui.add(inputs, BorderLayout.LINE_START);
		
		//Sundial component initialization
		drawFrame = new JFrame("drawing");
		drawPanel = new JPanel();
		drawPanel.setBackground(new Color(255, 255, 200));
		drawPanel.setBorder(BorderFactory.createMatteBorder(4, 4, 4, 4, Color.black));
		
		//Button customization
		buttons = new JPanel(new GridLayout(0, 5));
		
		createButton = new JButton("Create");
		createButton.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		createButton.addActionListener(this);
		printButton = new JButton("Print");
		printButton.setFont(new Font("Lucida Grande", Font.BOLD, 15));
		printButton.addActionListener(this);
		printButton.setPreferredSize(createButton.getPreferredSize());
		
		JPanel buttonPanel1 = new JPanel();
		buttonPanel1.add(createButton);
		buttonPanel1.setBackground(new Color(255, 255, 200));
		JPanel buttonPanel2 = new JPanel();
		buttonPanel2.add(printButton);
		buttonPanel2.setBackground(new Color(255, 255, 200));
		
		//Filler space for buttons
		JPanel empty1 = new JPanel();
		empty1.setBackground(new Color(255, 255, 200));
		JPanel empty2 = new JPanel();
		empty2.setBackground(new Color(255, 255, 200));
		JPanel empty3 = new JPanel();
		empty3.setBackground(new Color(255, 255, 200));
		
		buttons.add(empty1);
		buttons.add(buttonPanel1);
		buttons.add(empty2);
		buttons.add(buttonPanel2);
		buttons.add(empty3);
		buttons.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(4, 4, 4, 4, Color.black), BorderFactory.createEmptyBorder(4, 4, 4, 4)));
		buttons.setBackground(new Color(255, 255, 200));
		
		gui.add(buttons, BorderLayout.PAGE_END);
		gui.setVisible(true); //if more code is added for UI have this always be at end
	}

	/*
	 * Parses inputs and then performs actions based on whether create or print button were pressed.
	 * Displays error messages on invalid input.
	 *
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent event) {
		String stringLatitude = tfLatitude.getText();
		String stringLongitude = tfLongitude.getText();
		String stringDate = tfDate.getText();
		
		if (dlsNo.equals(event.getSource())) {
			dls = false;
		}
		else if (dlsYes.equals(event.getSource())) {
			dls = true;
		}		
		//seperating into else statement stops error message from popping up when user selects radios
		else {
			if (stringLatitude.length() < 1 || stringLongitude.length() < 1 || stringDate.length() < 1) {
				JOptionPane.showMessageDialog(gui, "Please do not leave any fields blank.", "Error", JOptionPane.ERROR_MESSAGE);
			} else {
				try {
					double numberLatitude = Double.parseDouble(stringLatitude);
					double numberLongitude = Double.parseDouble(stringLongitude);
					if (stringDate.length() > 6 && stringDate.length() < 9) {
						if (stringDate.substring(0, 1).equals("0")) {
							stringDate = stringDate.substring(1, stringDate.length());
						}
						int numberDate = Integer.parseInt(stringDate);
						createSundial(numberLatitude, numberLongitude, numberDate, dls);
					} else {
						JOptionPane.showMessageDialog(gui, "Please enter a valid date in MMDDYYYY format, where MM = month, DD = day, and YYYY = year.", "Error", JOptionPane.ERROR_MESSAGE);
					}
				} catch (NumberFormatException e) {
					JOptionPane.showMessageDialog(gui, "Please enter valid numbers for each field.\n" +
														"Latitude range: -90.0 to 90.0\n" +
														"Longtidue range: -180.0 to 180.0\n" +
														"Date: MMDDYYYY", "Error", JOptionPane.ERROR_MESSAGE);
				}
			
			}
		}
		
		if (createButton.equals(event.getSource())) {
			
			if (computations == null) {
				//do nothing
			}
			else {
				drawing = new BasicPanel(computations, drawFrame);
				gnomon = Gnomon.makeGnomon(computations);
				outputs.removeAll();
				outputs.addTab("Sundial Base", drawFrame.getContentPane());
				outputs.addTab("Gnomon", gnomon.getContentPane());
				gui.add(outputs, BorderLayout.CENTER);
				gui.revalidate();
				gui.repaint();
				sundialExists = true;
			}
		}
		if (printButton.equals(event.getSource()) && sundialExists) {
			PrinterJob pjob = PrinterJob.getPrinterJob();
			PageFormat preformat = pjob.defaultPage();
			PageFormat postformat = pjob.pageDialog(preformat);
			//If user does not hit cancel then print.
			if (preformat != postformat) {
			    //Set print component
			    pjob.setPrintable(new PrintHelper(drawFrame.getContentPane()), postformat);
			    if (pjob.printDialog()) {
			        try {
						pjob.print();
					} catch (PrinterException e) {
						JOptionPane.showMessageDialog(gui, "Error encountered while trying to print.", "Error", JOptionPane.ERROR_MESSAGE);
					}
			    }
			    pjob.setPrintable(new PrintHelper(gnomon.getContentPane()), postformat);
			    if (pjob.printDialog()) {
			    	try {
						pjob.print();
					} catch (PrinterException e) {
						JOptionPane.showMessageDialog(gui, "Error encountered while trying to print.", "Error", JOptionPane.ERROR_MESSAGE);
					}
			    }
			}

		}
		
	}
	
	/*
	 * Checks that latitude, longitude and date are valid before getting sundial calculations for hour lines.
	 */
	public void createSundial(double latitude, double longitude, int date, boolean dls) {
		int month = 0;
		int day = 0;
		int year = 0;
		int tempDate = date;
		
		if (latitude < -90.0 || latitude > 90.0) {
			JOptionPane.showMessageDialog(gui, "Please enter a valid latitude in the range: -90.0 to 90.0", "Error", JOptionPane.ERROR_MESSAGE);
		} else if (longitude < -180.0 || longitude > 180.0) {
			JOptionPane.showMessageDialog(gui, "Please enter a valid longitude in the range: -180.0 to 180.0", "Error", JOptionPane.ERROR_MESSAGE);
		}else {
			//Extract month, day and year
			tempDate = (tempDate/10000)*10000;
			year = date - tempDate;
			
			tempDate = (tempDate/1000000)*1000000 + year;
			day = (date - tempDate) / 10000;
			
			month = date / 1000000;
			
			//get angles here
			if (day == 0 || day > 31 || month == 0 || month > 12) {
				JOptionPane.showMessageDialog(gui, "Please enter a valid date.", "Error", JOptionPane.ERROR_MESSAGE);
			} else {
				computations = new SundialCompute(latitude, longitude, month, day, year, dls);
			}
		}
	}
	
	/*
	 * Nested class for printing sundial components.
	 */
	private class PrintHelper implements Printable {
		private Component c;
		
		public PrintHelper(Component c){
			this.c = c;
		}
		
		/*
		 * Image is scaled to width of page, so it assumes letter printing and not landscape.
		 */
		public int print(Graphics g, PageFormat format, int page) throws PrinterException {
			// only one page
			if(page > 0){
				return Printable.NO_SUCH_PAGE;
			}
			
			Dimension d = c.getSize();
			double imageWidth = d.getWidth();
			
			double pageWidth = format.getImageableWidth();
			
			double pageX = format.getImageableX();
			double pageY = format.getImageableY();
			
			double xRatio = pageWidth/imageWidth;
			
			Graphics2D g2D = (Graphics2D) g;
			g2D.translate(pageX, pageY);
			g2D.scale(xRatio, xRatio);
			c.print(g2D);
			
			return Printable.PAGE_EXISTS;
		}

	}
	
	public static void main(String[] args) {
		SundialInterface sundial = new SundialInterface();
		sundial.init();
	}

	public JFrame getGui() {
		return gui;
	}

	public void setGui(JFrame gui) {
		this.gui = gui;
	}

	public JPanel getInputs() {
		return inputs;
	}

	public void setInputs(JPanel inputs) {
		this.inputs = inputs;
	}

	public JPanel getButtons() {
		return buttons;
	}

	public void setButtons(JPanel buttons) {
		this.buttons = buttons;
	}

	public JFrame getDrawFrame() {
		return drawFrame;
	}

	public void setDrawFrame(JFrame drawFrame) {
		this.drawFrame = drawFrame;
	}

	public TextField getTfLatitude() {
		return tfLatitude;
	}

	public void setTfLatitude(TextField tfLatitude) {
		this.tfLatitude = tfLatitude;
	}

	public TextField getTfLongitude() {
		return tfLongitude;
	}

	public void setTfLongitude(TextField tfLongitude) {
		this.tfLongitude = tfLongitude;
	}

	public TextField getTfDate() {
		return tfDate;
	}

	public void setTfDate(TextField tfDate) {
		this.tfDate = tfDate;
	}

	public JRadioButton getDlsNo() {
		return dlsNo;
	}

	public void setDlsNo(JRadioButton dlsNo) {
		this.dlsNo = dlsNo;
	}

	public JRadioButton getDlsYes() {
		return dlsYes;
	}

	public void setDlsYes(JRadioButton dlsYes) {
		this.dlsYes = dlsYes;
	}

	public BasicPanel getDrawing() {
		return drawing;
	}

	public void setDrawing(BasicPanel drawing) {
		this.drawing = drawing;
	}

	public JButton getCreateButton() {
		return createButton;
	}

	public void setCreateButton(JButton createButton) {
		this.createButton = createButton;
	}

	public JButton getPrintButton() {
		return printButton;
	}

	public void setPrintButton(JButton printButton) {
		this.printButton = printButton;
	}

	public SundialCompute getComputations() {
		return computations;
	}

	public void setComputations(SundialCompute computations) {
		this.computations = computations;
	}
	
}